Skip to content

Kubernetes: manage the jump pod with a ReplicaSet - #4447

Merged
un-def merged 1 commit into
masterfrom
issue_4407_k8s_jump_pod_replica_set
Nov 2, 2026
Merged

un-def merged 1 commit into
masterfrom
issue_4407_k8s_jump_pod_replica_set

Conversation

@un-def

@un-def un-def commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

The jump pod was a bare pod, reused for as long as it existed. Once evicted, for example due to node pressure, it stayed in the Failed phase and was never recreated, so every job in the project failed until someone deleted the pod by hand.

The jump pod is now managed by a ReplicaSet, which replaces the pod after an eviction or a node removal. Not a Deployment: its ReplicaSet selects pods by the pod-template-hash label and thus cannot adopt the existing pod, and rollouts and rollbacks are not needed anyway.

  • run_job() creates the ReplicaSet if it doesn't exist. The ReplicaSet adopts a running legacy pod, a finished one is deleted. A pod created before Kubernetes: standardize object labeling #3891 has different labels and is left as is until it's gone.
  • A service created before Kubernetes: standardize object labeling #3891 selects pods by the old labels. Once the old pod is gone, the service is recreated with the new selector and the same NodePort, so that ssh_proxy of running jobs stays valid.
  • update_provisioning_data() lists jump pods by label and picks a ready one. It falls back to the name for a pod created before Kubernetes: standardize object labeling #3891 and fails right away if that pod is finished, as it's never replaced.
  • Creating the namespace, the ReplicaSet, and the service ignores 409 to tolerate concurrent run_job() calls.

Notes:

  • The Role now needs get/create on replicasets and list on pods. Without the update, Kubernetes provisioning fails with 403.
  • Once the legacy pod is gone, servers before this commit recreate a bare pod and the service on each run_job(). The ReplicaSet adopts and deletes the pod, and the NodePort changes if proxy_jump.port is not set. Downgrading requires deleting the ReplicaSet.

Part-of: #4407

The jump pod was a bare pod, reused for as long as it existed. Once
evicted, for example due to node pressure, it stayed in the Failed
phase and was never recreated, so every job in the project failed
until someone deleted the pod by hand.

The jump pod is now managed by a ReplicaSet, which replaces the pod
after an eviction or a node removal. Not a Deployment: its ReplicaSet
selects pods by the pod-template-hash label and thus cannot adopt the
existing pod, and rollouts and rollbacks are not needed anyway.

* run_job() creates the ReplicaSet if it doesn't exist. The ReplicaSet
  adopts a running legacy pod, a finished one is deleted. A pod created
  before #3891 has different labels and is left as is until it's gone.
* A service created before #3891 selects pods by the old labels. Once
  the old pod is gone, the service is recreated with the new selector
  and the same NodePort, so that ssh_proxy of running jobs stays valid.
* update_provisioning_data() lists jump pods by label and picks a ready
  one. It falls back to the name for a pod created before #3891 and
  fails right away if that pod is finished, as it's never replaced.
* Creating the namespace, the ReplicaSet, and the service ignores 409
  to tolerate concurrent run_job() calls.

Notes:

* The Role now needs get/create on replicasets and list on pods.
  Without the update, Kubernetes provisioning fails with 403.
* Once the legacy pod is gone, servers before this commit recreate
  a bare pod and the service on each run_job(). The ReplicaSet adopts
  and deletes the pod, and the NodePort changes if proxy_jump.port is
  not set. Downgrading requires deleting the ReplicaSet.

Part-of: #4407
@un-def
un-def merged commit e2f9100 into master Nov 2, 2026
29 checks passed
@un-def
un-def deleted the issue_4407_k8s_jump_pod_replica_set branch November 2, 2026 06:41
un-def added a commit that referenced this pull request Nov 3, 2026
The jump pod requested no resources, so it was BestEffort, the first
pod the kubelet evicts under node pressure. Since #4447, its ReplicaSet
recreates it, but every eviction still drops all SSH sessions through
the jump pod.

* The jump pod container now requests and limits CPU and memory
  equally, which gives it the Guaranteed QoS class. The defaults, 500m
  and 1Gi, come from measuring the jump pod image: ~9 MiB plus ~1.6 MiB
  per tunneled SSH session, and 130-180 MiB/s of tunneled traffic at
  500m.
* The new proxy_jump.cpu and proxy_jump.memory properties override the
  defaults. Values must be Kubernetes quantities of at least 100m and
  32Mi.
* The container also requests 64Mi of ephemeral storage, without a
  limit. QoS doesn't cover disk pressure, under which the kubelet first
  evicts pods whose disk usage exceeds their ephemeral-storage request.
* A jump pod that can't be scheduled, e.g., due to insufficient
  resources, is now logged as a warning with the scheduler's reason.
  Before, the only symptom was runs hitting the provisioning timeout.

Notes:

* New values don't apply to an existing jump pod. The docs describe
  how to apply them with kubectl.
* A legacy jump pod that the ReplicaSet adopts on upgrade stays
  BestEffort until it's recreated.

Fixes: #4407
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant